package edu.qhst.chat.netty;

import edu.qhst.chat.netty.cmdhandler.CmdHandlerFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


/**
 * netty服务器启动类
 *
 * @author 加浓美式
 * @Date 2023/7/4
 */
@Slf4j
@Component
public class NettyServer implements Runnable {
    @Value("${webSocket.server.port}")
    private Integer port;

    // boss处理客户端连接，有连接就建立一个channel，建立后将channel塞给work
    // work负责读写客户端消息，负责业务
    EventLoopGroup boss = new NioEventLoopGroup();
    EventLoopGroup work = new NioEventLoopGroup();

    @Override
    public void run() {
        // 工厂初始化，实现指令与处理器映射
        try {
            CmdHandlerFactory.init();
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);

        }
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, work).channel(NioServerSocketChannel.class)
                    // 配置TCP参数，握手字符串长度设置
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    // TCP_NODELAYT算法，尽可能发送大块数据，减少充斥的小块数据。
                    .option(ChannelOption.TCP_NODELAY, true)
                    // 开启心跳包活机制，客户端，服务端建立连接处于ESTABLISHED状态，超过2小时没有交流，机制会被启动。
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    // 配置固定长度接收缓存区分配器
                    .childOption(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(592048))
                    // 绑定I/O事件的处理类
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(
                                    new HttpServerCodec(), // http服务器编码器
                                    new HttpObjectAggregator(65535), //限制消息长度
                                    new WebSocketServerProtocolHandler("/chat"),
                                    new ChatMsgHandler() //自定义消息处理器
                            );
                        }
                    });
            // 注意: 实际项目中会使用 argArray 中的参数来指定端口号
            ChannelFuture future = bootstrap.bind(port).sync();
            if (future.isSuccess()) {
                log.info("netty服务器启动成功");
            }
            // 等待服务器信道关闭,
            // 也就是不要退出应用程序,
            // 让应用程序可以一直提供服务
            future.channel().closeFuture().sync();


        } catch (Exception e) {
            log.error(e.getMessage());
            boss.shutdownGracefully();
            work.shutdownGracefully();
            e.printStackTrace();
        }
    }
}
